package com.youboy.oauth.signature.method;

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;

import javax.crypto.Mac;
import javax.crypto.SecretKey;

import org.apache.commons.codec.binary.Base64;

import com.youboy.oauth.signature.InvalidSignatureException;
import com.youboy.oauth.signature.OAuthSignatureMethod;

/**
 * 
 * @author loudyn
 * 
 */
public class HMAC_SHA1SignatureMethod implements OAuthSignatureMethod {

	/**
	 * The name of this HMAC-SHA1 signature method ("HMAC-SHA1").
	 */
	public static final String SIGNATURE_NAME = "HMAC-SHA1";

	/**
	 * The MAC name (for interfacing with javax.crypto.*). "HmacSHA1".
	 */
	public static final String MAC_NAME = "HmacSHA1";

	private SecretKey key;

	public HMAC_SHA1SignatureMethod(SecretKey key) {
		this.key = key;
	}

	@Override
	public String getName() {
		return SIGNATURE_NAME;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.youboy.oauth.signature.OAuthSignatureMethod#verify(java.lang.String, java.lang.String)
	 */
	@Override
	public void verify(String signature, String signatureBaseString) throws InvalidSignatureException {
		try {

			byte[] signatureBytes = Base64.decodeBase64(signature.getBytes("UTF-8"));

			Mac mac = Mac.getInstance(MAC_NAME);
			mac.init(key);
			byte[] text = signatureBaseString.getBytes("UTF-8");
			byte[] calculatedBytes = mac.doFinal(text);
			if (!safeArrayEquals(calculatedBytes, signatureBytes)) {
				throw new InvalidSignatureException("Invalid signature!");
			}
		} catch (InvalidKeyException e) {
			throw new IllegalStateException(e);
		} catch (UnsupportedEncodingException e) {
			throw new IllegalStateException(e);
		} catch (NoSuchAlgorithmException e) {
			throw new IllegalStateException(e);
		}
	}

	boolean safeArrayEquals(byte[] a1, byte[] a2) {
		if (a1 == null || a2 == null) {
			return (a1 == a2);
		}

		if (a1.length != a2.length) {
			return false;
		}

		byte result = 0;
		for (int i = 0; i < a1.length; i++) {
			result |= a1[i] ^ a2[i];
		}

		return (result == 0);
	}
}
